<template>
  <div
    ref="luckyWheelWrapper"
    class="c-lucky--wheel-wrapper"
    :style="luckyWheelWrapperStyle"
  >
    <div @click="startRotating" class="wheel-pointer">
      <span class="text" :style="{ fontSize: tipTextFontSize }">{{
        tipText
      }}</span>
      <img class="icon" src="./images/lucky_wheel_point.png" alt="" />
    </div>
    <div
      ref="luckyWheel"
      class="lucky--wheel"
      @transitionend="handleTransitionend"
      :style="{
        transform: `rotate(${rotateAngle}deg)`,
        ...luckyWheelStyle,
      }"
    >
      <!--线-->
      <div
        class="line"
        v-for="(item, index) in prizeList"
        :key="index"
        :style="item.lineStyle"
      />
      <!--奖品列表-->
      <div class="prize-list">
        <div
          class="prize-item"
          v-for="(item, index) in prizeList"
          :key="index"
          :style="item.prizeStyle"
        >
          <img class="icon" :src="item.icon" alt="" />
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, reactive, ref, withDefaults } from "vue";
import type { PrizeItemType } from "./prizeList";
import { defaultPrizeList } from "./prizeList";
import { randomRange } from "@/utils/tools";

interface PrizeItemWithStyleType extends PrizeItemType {
  prizeStyle: string;
  lineStyle: string;
}

interface Props {
  prizes?: PrizeItemType[];
  // 总旋转时间 ms
  duration?: number | string;
  // 旋转圈数
  circle?: number;
  // 动画效果
  mode?:
    | "ease"
    | "ease-in"
    | "ease-out"
    | "ease-in-out"
    | "cubic-bezier(<number [0,1]>, <number>, <number [0,1]>, <number>)";
  // 是否禁止旋转
  disabled?: boolean;
  // 禁止旋转的提示
  message?: string;
  // 点击抽奖字体大小
  tipTextFontSize?: string;
  // 点击抽奖字样
  tipText?: string;
}

// eslint-disable-next-line no-undef
const emit = defineEmits<{
  (e: "start"): void;
  (e: "win", prize: PrizeItemType): void;
  (e: "miss", prize: PrizeItemType): void;
  (e: "finish", prize: PrizeItemType): void;
  (e: "trigger-disabled"): void; // 禁用点击的时候往外抛出的事件
}>();

const CIRCLE_ANGLE = 360;
let isRotating = false;
let prizeIndex = 0;

const luckyWheelWrapper = ref(null);

// 初始角度
const rotateAngle = ref(0);
// 奖品
const prizeList = ref<PrizeItemWithStyleType[]>([]);
// 记录每个奖项的角度范围
const angleList = ref<number[]>([]);

// eslint-disable-next-line no-undef
const props = withDefaults(defineProps<Props>(), {
  // 奖品列表，可以从接口获取
  prizes: () => defaultPrizeList,
  duration: 3000,
  circle: 5,
  mode: "ease-out",
  disabled: false,
  message: "次数已经用完",
  tipText: "点击\n抽奖",
  tipTextFontSize: "20px",
});

const luckyWheelStyle = ref<{ transition: string }>({
  transition: "",
});

const luckyWheelWrapperStyle = reactive<{ width?: string; height?: string }>(
  {}
);

onMounted(() => {
  const wrapperSize = setStyle();

  prizeList.value = props.prizes.map((it, i, list) => {
    const l = list.length;
    // 计算单个奖项所占的角度
    const average = CIRCLE_ANGLE / l;
    const half = average / 2;
    // 每个奖项旋转的位置为 当前 i * 平均值 + 平均值 / 2
    const angle = -(i * average + half);
    // 弦长：sin(角度/2) * 2 * 半径
    const prizeWidth =
      Math.sin((half * Math.PI * 2) / CIRCLE_ANGLE) * parseFloat(wrapperSize);
    // 奖品样式
    const prizeStyle = `transform: translateX(-50%) rotate(${angle}deg); width: ${prizeWidth}px`;
    // 线条样式
    const lineStyle = `transform: translateX(-50%) rotate(${average * i}deg)`;
    // 记录每个奖项的角度范围
    angleList.value.push(i * average + half);

    return {
      prizeStyle,
      lineStyle,
      ...it,
    };
  });
});

function setStyle(): string {
  const wrapperSize = window.getComputedStyle(luckyWheelWrapper.value).width;
  luckyWheelWrapperStyle.width = wrapperSize;
  luckyWheelWrapperStyle.height = wrapperSize;

  return wrapperSize;
}

function startRotating() {
  if (props.disabled) {
    emit("trigger-disabled");
    return;
  }

  if (isRotating) return;

  emit('start')

  isRotating = true;

  luckyWheelStyle.value.transition = `${props.duration}ms ${props.mode}`;

  prizeIndex = randomRange(0, prizeList.value.length);

  // 计算角度
  rotateAngle.value =
    rotateAngle.value +
    // 多旋转的圈数
    props.circle * CIRCLE_ANGLE +
    // 奖项的角度
    angleList.value[prizeIndex] -
    (rotateAngle.value % CIRCLE_ANGLE);
}

function handleTransitionend() {
  isRotating = false;

  // 动画结束时需要移除 transition,否则屏幕尺寸改变也会触发动画
  luckyWheelStyle.value.transition = "";

  const prize = prizeList.value[prizeIndex];
  if (prize.isPrize) {
    emit("win", prize);
  } else {
    emit("miss", prize);
  }
  emit("finish", prize);
}

// eslint-disable-next-line no-undef
defineExpose({
  startRotating,
});
</script>

<!--必须写下面,否则无效-->
<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  name: "VunLuckyWheel",
});
</script>

<style scoped lang="scss">
.c-lucky--wheel-wrapper {
  position: relative;
  box-sizing: border-box;
}

.lucky--wheel {
  $padding: 10%;

  display: flex;
  position: relative;
  width: 100%;
  height: 100%;
  padding: $padding;
  background: linear-gradient(white, white),
    url("./images/lucky_wheel_bg.png") no-repeat center;
  background-size: cover;
  background-clip: content-box, border-box;
  box-sizing: border-box;
  border-radius: 50%;

  .line {
    position: absolute;
    top: $padding;
    left: 50%;
    width: 8px;
    height: calc((100% - $padding * 2) / 2);
    transform-origin: center bottom;
    background: #ffd9d0;
  }
}

.wheel-pointer {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 20%;
  height: 20%;
  @include transform-center();
  z-index: 1;

  .icon {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
  }

  .text {
    display: block;
    white-space: pre-wrap;
    position: relative;
    color: #ea3d18;
    letter-spacing: 2px;
    font-weight: 600;
    text-align: center;
    z-index: 2;
  }
}

.prize-list {
  position: relative;
  width: 100%;
  height: 100%;

  .prize-item {
    @include transform-center-horizontal();
    height: 50%;
    text-align: center;
    transform-origin: bottom center;

    .icon {
      width: 30%;
      margin-top: 15%;
    }
  }
}
</style>
